﻿namespace LiveLabs.Logging.Util
{
    using System;
    using Xunit;

    public class ClockFixture
    {
        [Fact]
        public void Should_Return_Local_Time_From_Now_When_Set_With_Local_Time()
        {
            Action test = () =>
            {
                DateTime now = new DateTime(2009, 2, 2, 10, 10, 10, DateTimeKind.Local);
                Clock.SetNow(now);

                DateTime newNow = Clock.Now;
                Assert.Equal(now, newNow);
                Assert.Equal(DateTimeKind.Local, newNow.Kind);
            };

            AlwaysResetClock(test);
        }

        [Fact]
        public void Should_Return_Local_Time_From_Now_When_Set_With_Utc_Time()
        {
            Action test = () =>
            {
                DateTime now = new DateTime(2009, 2, 2, 10, 10, 10, DateTimeKind.Utc);
                Clock.SetNow(now);

                DateTime newNow = Clock.Now;
                Assert.Equal(now.ToLocalTime(), newNow);
                Assert.Equal(DateTimeKind.Local, newNow.Kind);
            };

            AlwaysResetClock(test);
        }

        [Fact]
        public void Should_Return_Local_Time_From_Now_When_Set_With_Local_Time_Provider()
        {
            Action test = () =>
            {
                DateTime now = new DateTime(2009, 2, 2, 10, 10, 10, DateTimeKind.Local);
                bool providerExecuted = false;

                Func<DateTime> provider = () =>
                {
                    providerExecuted = true;
                    return now;
                };

                Clock.SetNowProvider(provider);

                DateTime newNow = Clock.Now;
                Assert.True(providerExecuted);
                Assert.Equal(now, newNow);
                Assert.Equal(DateTimeKind.Local, newNow.Kind);
            };

            AlwaysResetClock(test);
        }

        [Fact]
        public void Should_Return_Local_Time_From_Now_When_Set_With_Utc_Time_Provider()
        {
            Action test = () =>
            {
                DateTime now = new DateTime(2009, 2, 2, 10, 10, 10, DateTimeKind.Utc);
                bool providerExecuted = false;

                Func<DateTime> provider = () =>
                {
                    providerExecuted = true;
                    return now;
                };

                Clock.SetNowProvider(provider);

                DateTime newNow = Clock.Now;
                Assert.True(providerExecuted);
                Assert.Equal(now.ToLocalTime(), newNow);
                Assert.Equal(DateTimeKind.Local, newNow.Kind);
            };

            AlwaysResetClock(test);
        }

        [Fact]
        public void Should_Return_Utc_Time_From_UtcNow_When_Set_With_Utc_Time()
        {
            Action test = () =>
            {
                DateTime now = new DateTime(2009, 2, 2, 10, 10, 10, DateTimeKind.Utc);
                Clock.SetUtcNow(now);

                DateTime newNow = Clock.UtcNow;
                Assert.Equal(now, newNow);
                Assert.Equal(DateTimeKind.Utc, now.Kind);
                Assert.Equal(DateTimeKind.Utc, newNow.Kind);
            };

            AlwaysResetClock(test);
        }

        [Fact]
        public void Should_Return_Utc_Time_From_UtcNow_When_Set_With_Local_Time()
        {
            Action test = () =>
            {
                DateTime now = new DateTime(2009, 2, 2, 10, 10, 10, DateTimeKind.Local);
                Clock.SetUtcNow(now);

                DateTime newNow = Clock.UtcNow;
                Assert.Equal(now.ToUniversalTime(), newNow);
                Assert.Equal(DateTimeKind.Local, now.Kind);
                Assert.Equal(DateTimeKind.Utc, newNow.Kind);
            };

            AlwaysResetClock(test);
        }

        [Fact]
        public void Should_Return_Utc_Time_From_UtcNow_When_Set_With_Utc_Time_Provider()
        {
            Action test = () =>
            {
                DateTime now = new DateTime(2009, 2, 2, 10, 10, 10, DateTimeKind.Utc);
                bool providerExecuted = false;

                Func<DateTime> provider = () =>
                {
                    providerExecuted = true;
                    return now;
                };

                Clock.SetUtcNowProvider(provider);

                DateTime newNow = Clock.UtcNow;
                Assert.True(providerExecuted);
                Assert.Equal(now, newNow);
                Assert.Equal(DateTimeKind.Utc, now.Kind);
                Assert.Equal(DateTimeKind.Utc, newNow.Kind);
            };

            AlwaysResetClock(test);
        }

        [Fact]
        public void Should_Return_Utc_Time_From_UtcNow_When_Set_With_Local_Time_Provider()
        {
            Action test = () =>
            {
                DateTime now = new DateTime(2009, 2, 2, 10, 10, 10, DateTimeKind.Local);
                bool providerExecuted = false;

                Func<DateTime> provider = () =>
                {
                    providerExecuted = true;
                    return now;
                };

                Clock.SetUtcNowProvider(provider);

                DateTime newNow = Clock.UtcNow;
                Assert.True(providerExecuted);
                Assert.Equal(now.ToUniversalTime(), newNow);
                Assert.Equal(DateTimeKind.Local, now.Kind);
                Assert.Equal(DateTimeKind.Utc, newNow.Kind);
            };

            AlwaysResetClock(test);
        }

        private void AlwaysResetClock(Action test)
        {
            try
            {
                test();
            }
            finally
            {
                Clock.Reset();
            }
        }
    }
}
